package io.jsen.meta.diff.obj.complex;

import io.jsen.meta.diff.field.MetaField;
import io.jsen.meta.diff.obj.MetaObject;
import io.jsen.meta.diff.obj.abs.AbstractComplex;
import io.jsen.meta.diff.obj.special.MetaDelete;
import io.jsen.meta.diff.obj.special.MetaNull;
import io.jsen.meta.diff.support.MetaModifier;
import io.jsen.meta.diff.utils.Md5Util;
import lombok.EqualsAndHashCode;

import java.util.List;
import java.util.Map;
import java.util.TreeSet;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author jsen
 * @date 2021/4/6
 */
@EqualsAndHashCode(callSuper = true)
public class MetaCollection extends AbstractComplex<MetaCollection, List<MetaObject<?, ?>>> {

    public MetaCollection(MetaField metaField, List<MetaObject<?, ?>> val) {
        super(metaField, val);
        this.index = createIndex();
        this.modifier = MetaModifier.COLLECTION;
        this.val.forEach(metaObject -> metaObject.getMetaRelation().setParent(this));
    }

    @Override
    protected int calState(MetaCollection obj) {
        Map<String, MetaObject<?, ?>> self = val.stream().collect(Collectors.toMap(MetaObject::getIndex,
                Function.identity(), (a, b) -> a));
        Map<String, MetaObject<?, ?>> other = obj.val.stream().collect(Collectors.toMap(MetaObject::getIndex,
                Function.identity(), (a, b) -> a));
        AtomicInteger size = new AtomicInteger(val.size());
        self.forEach((index, metaObject) -> {
            if (other.containsKey(index)) {
                metaObject.doDiff(other.get(index));
                other.remove(index);
            } else {
                metaObject.doDiff(new MetaNull(
                        new MetaField(metaObject.getMetaRelation().getMetaField().getFieldDefine(),
                                metaObject.getMetaRelation().getMetaField().getKey())
                ));
            }
        });
        other.forEach((index, metaObject) -> {
            MetaDelete del = new MetaDelete(
                    new MetaField(getMetaRelation().getMetaField().getFieldDefine(), size.getAndAdd(1))
            );
            del.doDiff(metaObject);
            val.add(del);
        });


        return calCollectionState(val);
    }

    private String createIndex() {
        StringBuilder builder = new StringBuilder();
        TreeSet<String> sortIndex = new TreeSet<>();
        for (MetaObject<?, ?> obj : val) {
            sortIndex.add(obj.getIndex());
        }
        for (String index : sortIndex) {
            builder.append(index);
        }
        return Md5Util.md5(builder.toString());
    }

    @Override
    @SuppressWarnings("rawtypes")
    public MetaObject doDiff(MetaObject obj) {
        super.doDiff(obj);
        if (obj instanceof MetaNull) {
            val.forEach(metaObject -> metaObject.doDiff(new MetaNull(
                    new MetaField(metaObject.getMetaRelation().getMetaField().getFieldDefine(),
                            metaObject.getMetaRelation().getMetaField().getKey())
            )));
        }
        return this;
    }
}
